/*
 *  Copyright (C) 2010 Jonathan MERCIER <bioinfornatics@fedoraproject.org>,
 *  Louise-Amélie SCHMITT <louise-amelie.schmitt@etu.u-bordeaux1.fr>,
 *  Florence Maurier <florence_maurier@orange.fr>,
 *  Marianne Dussart <marianne.dussart@orange.fr>,
 *  Nicolas Fontaine <nifontaine@gmail.com>
 *
 *  JACoMode is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  JACoMode is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with JACoMode.  If not, see <http://www.gnu.org/licenses/>.
 */
package fr.ubdx1.jacomode.clientSideApplet;

import fr.ubdx1.jacomode.models.GraphMotifs;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.miv.graphstream.graph.Edge;
import org.miv.graphstream.graph.Node;
import org.miv.graphstream.ui.GraphViewerListener;
import org.miv.graphstream.ui.GraphViewerRemote;
import org.miv.graphstream.ui.swing.SwingGraphViewer;

/**
 *
 * @author Louise-Amélie SCHMITT
 */
public class GraphDisplay extends JApplet implements ActionListener, GraphViewerListener
{

    /**
     * Initialization method that will be called after the applet is loaded
     * into the browser.
     */
    private static final long serialVersionUID = 8L;
    private GraphMotifs graph = new GraphMotifs(false, true);
    private SwingGraphViewer viewer;
    private GraphViewerRemote remote;
    private JButton button_auto = new JButton("Auto Layout");
    private JButton button_manual = new JButton("Hasse diagram Layout");
    private JComponent affichage;
    private JPanel south = new JPanel();
    private JLabel info = new JLabel("Applet loading...");
    private boolean running = true;
    private Integer count = 0;
    public static final String emergency_css =
	    "graph {color:#FF7272; text-style:bold; padding:100;}"
	    + "node {node-shape:text-box; text-color:black; border-color:black; border-width:1px;}";

    @Override
    public void init()
    {

//        // Retrieving the graph
//        getGraph();

	// Restoring the graph
	for (int i = 0; true; i++) {
	    Integer param = new Integer(i);
	    String paramName = "param" + param.toString();
	    String motif = getParameter(paramName);
	    if (motif == null) {
		break;
	    }
	    graph.AddMotif(motif, motif);
	    count++;
	}
	if (count == 0) {
	    // If the graph is empty, set an error graph
	    errorGraph();
	}
	graph.PackGraph();



	// Configuring the viewer thread and display
	viewer = new SwingGraphViewer(graph, true /*autoLayout*/, true /*isPanel*/);
	remote = viewer.newViewerRemote();
	affichage = (JComponent) viewer.getComponent();

	// Applet properties
	this.setSize(800, 600);
	this.getContentPane().add(affichage, BorderLayout.CENTER);
	this.getContentPane().add(info, BorderLayout.NORTH);
	button_auto.addActionListener(this);
	button_manual.addActionListener(this);
	south.add(button_auto);
	south.add(button_manual);
	colorComponent(affichage, "Y");
	colorComponent(south, "Y");
	info.setOpaque(true);
	colorComponent(info, "Y");
	colorComponent(info, "R");
	this.getContentPane().add(south, BorderLayout.SOUTH);
	setVisible(true);

	if (running) {
	    // Setting graph info on label
	    colorComponent(info, "G");
	    Integer edgecount = graph.getEdgeCount();
	    Integer nodecount = graph.getNodeCount();
	    info.setText("Graph loaded with " + count + "motifs: " + nodecount.toString() + " nodes, " + edgecount.toString() + "edges");
	}
    }

    @Override
    public void start()
    {
	// GraphViewer properties
	remote.setQuality(4);	// 4 is the max quality (range is 0-4).
	remote.addViewerListener(this);

	// Listening to the interactions mouse-graph
	running = true;
	while (running) {
	    remote.pumpEvents();
	    try {
		Thread.sleep(100);
	    } catch (Exception e) {
	    }
	}
    }

    @Override
    public void stop()
    {
	running = false;
    }

    @Override
    public void destroy()
    {
	running = false;
    }

//    // Restoring the graph from the applet's <param> tags
//    private void makeGraph() {
//        graph = new GraphMotifs(false, true);
////        String[][] paramInfo = null;
////        paramInfo = this.getParameterInfo();
////        int nbParam = paramInfo.length;
//        for (int i = 0; true; i++) {
//            Integer param = new Integer(i);
//            String paramName = "param" + param.toString();
//            System.out.println(paramName);
//            String motif = this.getParameter(paramName);
//            if (motif == null) {
//                break;
//            }
//            System.out.println(motif);
////                try {
////                    info.setText(motif);
////                } catch (Exception e) {
////                    info.setText("can't display motif");
////                }
//            graph.AddMotif(motif, motif);
//        }
//        graph.PackGraph();
//    }
//    // Getting the graph object from servlet
//    private void getGraph() {
//        // connect to the servlet
//        URL results_servlet;
//        try {
//            String page = getDocumentBase().toString();
//            page = replace(page, "acomGlobalResults", "sendGraph");
//            results_servlet = new URL(page);
//            URLConnection servletConnection;
//            try {
//                servletConnection = results_servlet.openConnection();
//                // Don't used a cached version of URL connection.
//                servletConnection.setUseCaches(false);
//                servletConnection.setDefaultUseCaches(false);
//                // Read the input from the servlet.
//                ObjectInputStream inputFromServlet = new ObjectInputStream(servletConnection.getInputStream());
//                try {
//                    graph = (GraphMotifs) inputFromServlet.readObject();
//                    colorComponent(info, "G");
//                    Integer nb = graph.getEdgeCount();
//                    info.setText(nb.toString());
//                } catch (ClassNotFoundException ex) {
//                    info.setText("ERROR: Can't read object");
////                    Logger.getLogger(GraphDisplay.class.getName()).log(Level.SEVERE, null, ex);
//                    errorGraph();
//                }
//            } catch (IOException ex) {
//                info.setText("ERROR: Can't connect to servlet");
////                Logger.getLogger(GraphDisplay.class.getName()).log(Level.SEVERE, null, ex);
//                errorGraph();
//            }
//        } catch (MalformedURLException ex) {
//            info.setText("ERROR: Can't find servlet");
////            Logger.getLogger(GraphDisplay.class.getName()).log(Level.SEVERE, null, ex);
//            errorGraph();
//        }
//    }
//    // Substring replacement method
//    private static String replace(String originalText, String subStringToFind, String subStringToReplaceWith) {
//        int s = 0;
//        int e = 0;
//
//        StringBuffer newText = new StringBuffer();
//
//        while ((e = originalText.indexOf(subStringToFind, s)) >= 0) {
//
//            newText.append(originalText.substring(s, e));
//            newText.append(subStringToReplaceWith);
//            s = e + subStringToFind.length();
//
//        }
//        return newText.toString();
//    }
    // Sets a void graph object in case the real graph cannot be retrieved from servlet
    public void errorGraph()
    {
	running = false;
	graph = new GraphMotifs(false, true);
	graph.addAttribute("ui.stylesheet", emergency_css);
	Node errorNode = graph.addNode("errorNode");
	errorNode.addAttribute("label", "Empty graph!");
	errorNode.addAttribute("motifs", "empty");
	colorComponent(info, "R");
	info.setText("WARNING: For some reason, the graph is empty. You might not have any motif with the values you entered.");
    }

    // Sets the components' color
    private void colorComponent(JComponent component, String color)
    {
	if (color.equals("Y")) {
	    //component.setBackground(new Color(255, 240, 220));
	    component.setBackground(new Color(255, 255, 255));
	} else if (color.equals("G")) {
	    component.setForeground(new Color(186, 210, 124));
	} else if (color.equals("R")) {
	    component.setForeground(new Color(145, 0, 11));
	}
    }

    // Methods inherited from ActionListener
    public void actionPerformed(ActionEvent e)
    {
	if (e.getSource() == button_auto) {
	    toggleLayout(true);
	}
	if (e.getSource() == button_manual) {
	    toggleLayout(false);
	}
    }

    // Toggle graph layout when buttons are clicked
    private void toggleLayout(boolean autolayout)
    {
	affichage.setVisible(false);
	this.getContentPane().remove(affichage);
	viewer = new SwingGraphViewer(graph, autolayout /*autoLayout*/, true /*isPanel*/);
	affichage = (JComponent) viewer.getComponent();
	this.getContentPane().add(affichage, BorderLayout.CENTER);
	remote = viewer.newViewerRemote();
	remote.addViewerListener(this);
	affichage.setVisible(true);
    }

    //  Methods inherited from GraphViewerListener
    public void nodeSelected(String nodeID, boolean selected)
    {
	Node selectedNode = graph.getNode(nodeID);
	String motifsCovered = (String) selectedNode.getAttribute("motifs");
	String[] motifsList = motifsCovered.split(",");

	if (selected) {

	    Iterator<? extends Node> nodes = graph.getNodeIterator();

	    while (nodes.hasNext()) {
		Node currentNode = nodes.next();
		motifsCovered = (String) currentNode.getAttribute("motifs");
		for (int i = 0; i < motifsList.length; i++) {
		    if (motifsCovered.contains(motifsList[i])) {
			currentNode.addAttribute("ui.class", "click");
		    }
		}
	    }

	    Iterator<? extends Edge> edges = graph.getEdgeIterator();

	    while (edges.hasNext()) {
		Edge currentEdge = edges.next();
		motifsCovered = (String) currentEdge.getAttribute("motifs");
		for (int i = 0; i < motifsList.length; i++) {
		    if (motifsCovered.contains(motifsList[i])) {
			currentEdge.addAttribute("ui.class", "click");
		    }
		}
	    }

	} else {

	    Iterator<? extends Node> nodes = graph.getNodeIterator();

	    while (nodes.hasNext()) {
		Node currentNode = nodes.next();
		motifsCovered = (String) currentNode.getAttribute("motifs");
		for (int i = 0; i < motifsList.length; i++) {
		    if (motifsCovered.contains(motifsList[i])) {
			currentNode.changeAttribute("ui.class", "active");
		    }
		}
	    }

	    Iterator<? extends Edge> edges = graph.getEdgeIterator();

	    while (edges.hasNext()) {
		Edge currentEdge = edges.next();
		motifsCovered = (String) currentEdge.getAttribute("motifs");
		for (int i = 0; i < motifsList.length; i++) {
		    if (motifsCovered.contains(motifsList[i])) {
			currentEdge.changeAttribute("ui.class", "active");
		    }
		}
	    }
	}
    }

    public void spriteSelected(String arg0, boolean arg1)
    {
//        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void nodeMoved(String arg0, float arg1, float arg2, float arg3)
    {
//        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void spriteMoved(String arg0, float arg1, float arg2, float arg3)
    {
//        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void backgroundClicked(float x, float y, int button, boolean clicked)
    {
	if (!clicked) {
	    Iterator<? extends Node> nodes = graph.getNodeIterator();

	    while (nodes.hasNext()) {
		nodes.next().removeAttribute("ui.class");
	    }

	    Iterator<? extends Edge> edges = graph.getEdgeIterator();

	    while (edges.hasNext()) {
		edges.next().removeAttribute("ui.class");
	    }
	}
    }
    // TODO overwrite start(), stop() and destroy() methods
}
